﻿namespace ZDS

open System
open System.Collections
open System.Collections.Generic

module ZShareStackModule =
    type ZArrayStackRevIterator<'t>(m_array: 't[],m_top2) =
        let mutable m_index = m_array.Length

        interface IDisposable with
            member this.Dispose() =
                ()

        interface IEnumerator<'t> with
            member this.Current =
                m_array.[m_index]

        interface IEnumerator with
            member this.MoveNext() =
                m_index <- m_index - 1
                m_index >= m_top2

            member this.Reset() =
                m_index <- m_array.Length

            member this.Current =
                box (this :> IEnumerator<'t>).Current

    type ZShareStackAscEnumerable<'t>(m_array : 't[],m_top1) =
        interface IEnumerable<'t> with
            member this.GetEnumerator() =
                new ZArrStackIterator<'t>(m_array,m_top1) :> IEnumerator<'t>

        interface IEnumerable with
            member this.GetEnumerator() =
                new ZArrStackIterator<'t>(m_array,m_top1) :> IEnumerator

    type ZShareStackDesEnumerable<'t>(m_array : 't[],m_top2) = 
        interface IEnumerable<'t> with
            member this.GetEnumerator() =
                new ZArrayStackRevIterator<'t>(m_array,m_top2) :> IEnumerator<'t>

        interface IEnumerable with
            member this.GetEnumerator() =
                new ZArrayStackRevIterator<'t>(m_array,m_top2) :> IEnumerator

open ZShareStackModule
type ZShareStack<'t>(m_capacity)=
    // ----------------- primary constructor
    let m_array = Array.zeroCreate<'t>(m_capacity)
    let mutable m_top1 = -1
    let mutable m_top2 = m_capacity

    // ----------------- members
    member this.GetSize stackNo =
        match stackNo with
            | 1 -> 
                m_top1 + 1
            | 2 -> 
                m_capacity - m_top2
            | _ ->
                raise <| ArgumentOutOfRangeException("stack number can only be 1 or 2")

    member this.Push stackNo value =
        if (m_top1 + 1) = m_top2 then
            raise <| ZError(ErrorCode.Full)

        match stackNo with
            | 1 -> 
                m_top1 <- m_top1 + 1
                m_array.[m_top1] <- value
            | 2 -> 
                m_top2 <- m_top2 - 1
                m_array.[m_top2] <- value
            | _ -> 
                raise <| ArgumentOutOfRangeException("stack number can only be 1 or 2")

    member this.Pop stackNo =
        match stackNo with
            | 1 -> 
                if m_top1 = -1 then
                    raise <| ZError(ErrorCode.Empty)

                let value = m_array.[m_top1]
                m_top1 <- m_top1 - 1
                value

            | 2 -> 
                if m_top2 = m_capacity then
                    raise <| ZError(ErrorCode.Empty)

                let value = m_array.[m_top2]
                m_top2 <- m_top2 + 1
                value
            | _ ->
                raise <| ArgumentOutOfRangeException("stack number can only be 1 or 2")

    member this.GetEnumerable stackNo  =
        match stackNo with
            | 1 -> 
                new ZShareStackAscEnumerable<'t>(m_array,m_top1) :> IEnumerable<'t>
            | 2 -> 
                new ZShareStackDesEnumerable<'t>(m_array,m_top2) :> IEnumerable<'t>
            | _ ->
                raise <| ArgumentOutOfRangeException("stack number can only be 1 or 2")
                
                

